#ifndef TIMEINTEGRATOR_
#define TIMEINTEGRATOR_

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cmath>

#include "DGMatrix.h"
#include "DGIntMatrix.h"
#include "ANN/ANN.h"
#include "Parser/Parser.h"
#include "functionSpace.h"
#include "saveGmsh.h"
#include "model.h"

using namespace std;

//typedef map<int,DGMatrix*>      SolContainer;
//typedef map<int,FunctionSpace*> FsContainer;

enum RKType {FE,RK22,RK33,RK44,SSP22,SSP32,SSP33};
//------------------------------------------------------------------------------
class TimeIntegrator{

private:
  
public:
  
  TimeIntegrator(Model*,ClassParameter&);
  ~TimeIntegrator(){};
  
  virtual void      advanceInTime(InitialSolution*);
  virtual DGVector* computeLnorm(const SolContainer*,InitialSolution*);
  
protected:
  
  Model* model;
  
  int nbTimeStep;
  int Step;
  int outputFreq;
  
  double dt;
  double realTime;
  
  virtual int nbSubStep  ()            const=0;
  virtual double aij     (int i,int j) const=0;
  virtual double bi      (int i)       const=0;     
  
};
//------------------------------------------------------------------------------
class ForwardEuler:public TimeIntegrator{
  
private:
  
  static double coefficients[3][1];
  
public:
  
  ForwardEuler(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  int nbSubStep  ()            const{return 1;}
  double aij     (int i,int j) const{return coefficients[i][j];} 
  double bi      (int i)       const{return coefficients[1][i];} 

  
};
//------------------------------------------------------------------------------
class RungeKutta22:public TimeIntegrator{
  
private:
  
  static double coefficients[4][2];
  
public:
  
  RungeKutta22(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  virtual int nbSubStep  ()            const{return 2;}
  virtual double aij     (int i,int j) const{return coefficients[i][j];} 
  virtual double bi      (int i)       const{return coefficients[2][i];} 
  
};
//------------------------------------------------------------------------------
class SSPRungeKutta22:public TimeIntegrator{
  
private:
  
  static double coefficients[4][2];
  
public:
  
  SSPRungeKutta22(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  virtual int nbSubStep  ()            const{return 2;}
  virtual double aij     (int i,int j) const{return coefficients[i][j];} 
  virtual double bi      (int i)       const{return coefficients[2][i];} 
  
  // SSPmm = m order, m stages:
  //     
  //     u(i) = u(i-1) + dt*R[u(i-1)]   i=1,...,m-1     FE steps 
  //     
  //      u(n+1) =
  //
  
};
//------------------------------------------------------------------------------
class RungeKutta33:public TimeIntegrator{
  
private:
  
  static double coefficients[5][3];
  
public:
  
  RungeKutta33(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  int nbSubStep  ()            const{return 3;}
  double aij     (int i,int j) const{return coefficients[i][j];} 
  double bi      (int i)       const{return coefficients[3][i];} 
  
};
//------------------------------------------------------------------------------
class SSPRungeKutta32:public TimeIntegrator{
  
private:
  
  static double coefficients[5][3];
  
public:
  
  SSPRungeKutta32(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  int nbSubStep  ()            const{return 3;}
  double aij     (int i,int j) const{return coefficients[i][j];} 
  double bi      (int i)       const{return coefficients[3][i];} 
  
};
//------------------------------------------------------------------------------
class SSPRungeKutta33:public TimeIntegrator{
  
private:
  
  static double coefficients[5][3];
  
public:
  
  SSPRungeKutta33(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  int nbSubStep  ()            const{return 3;}
  double aij     (int i,int j) const{return coefficients[i][j];} 
  double bi      (int i)       const{return coefficients[3][i];} 
  
};
//------------------------------------------------------------------------------
class RungeKutta44:public TimeIntegrator{
  
private:
  
  static double coefficients[6][4];
  
public:
  
  RungeKutta44(ClassParameter& param,Model* model):TimeIntegrator(model,param){};
  
  int nbSubStep  ()            const{return 4;}
  double aij     (int i,int j) const{return coefficients[i][j];} 
  double bi      (int i)       const{return coefficients[4][i];} 
  
};
//------------------------------------------------------------------------------
class TimeIntegratorFactory{
public:
  static TimeIntegrator* CreateNewTimeIntegrator(Model*,ClassParameter&);
};

#endif
